using System;
using System.Text.RegularExpressions;
using System.Xml;
using System.Xml.Schema;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;

namespace pginstaller.tasks
{
    /*
     * Validate the translation XML, per schema, parameter numbers and parameter types
     */
    public class ValidateTranslation : Task
    {
        /* List of files to validate */
        [Required]
        public string[] Sources
        {
            get { return _sources; }
            set { _sources = value; }
        }

        /* Exclude any specific file? It's kinda hard to do in the XML.. */
        public string Exclude
        {
            get { return _exclude; }
            set { _exclude = value; }
        }

        /* Internal storage for the properties */
        private string[] _sources;
        private string _exclude = "";

        /* Precompile a regexp to match the parameters */
        private static Regex reParam = new Regex("%.", RegexOptions.Compiled);

        /* Perform all validation before we return, so store a global error status */
        private bool schemaerror = false;
        
        /* Perform the actual build task */
        public override bool Execute()
        {
            XmlSchemaSet schemas = LoadSchema();
            foreach (string srcfile in _sources)
            {
                if (srcfile == _exclude)
                    continue;

                Log.LogMessage("Validating translation for {0}", srcfile);

                XmlReaderSettings xsettings = new XmlReaderSettings();
                xsettings.ValidationType = ValidationType.Schema;
                xsettings.Schemas = schemas;
                xsettings.ValidationEventHandler += new ValidationEventHandler(ValidationCallback);

                try
                {
                    XmlReader rdr = XmlReader.Create(srcfile + ".xml", xsettings);

                    while (rdr.Read())
                    {
                        if (rdr.NodeType == XmlNodeType.Element && rdr.Name == "string")
                        {
                            MatchCollection origmatches = reParam.Matches(rdr.GetAttribute("original"));
                            MatchCollection transmatches = reParam.Matches(rdr.GetAttribute("translation"));
                            if (origmatches.Count != transmatches.Count)
                            {
                                Log.LogError("Translation for '{0}' has incorrect number of parameters, {1} instead of {2}.",
                                    rdr.GetAttribute("original"),
                                    transmatches.Count, origmatches.Count);
                                schemaerror = true;
                                continue;
                            }
                            for (int i = 0; i < origmatches.Count; i++)
                            {
                                if (origmatches[i].Value != transmatches[i].Value)
                                {
                                    Log.LogError("Translation for '{0}', has invalid type for parameter {1}, {2} instead of {3}.",
                                        rdr.GetAttribute("original"), i,
                                        transmatches[i].Value, origmatches[i].Value);
                                    schemaerror = true;
                                }
                            }
                        }
                    }
                    rdr.Close();
                }
                catch (XmlException ex)
                {
                    Log.LogError("XML load error: {0}", ex.Message);
                    schemaerror = true;
                }
            }

            if (schemaerror)
                return false;

            return true;
        }

        private void ValidationCallback(object sender, ValidationEventArgs e)
        {
            Log.LogError("Schema validation error: {0}", e.Message);
            schemaerror = true;
        }

        private XmlSchemaSet LoadSchema()
        {
            XmlSchemaSet sc = new XmlSchemaSet();
            sc.Add("http://pginstaller.projects.postgresql.org/xmlns/translation", "translation.xsd");
            sc.Compile();
            return sc;
        }
    }
}